#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MCPy 模板系统

处理 Python 的泛型类型（TypeVar）到 C++ 模板的转换
"""

from dataclasses import dataclass, field
from typing import List, Optional, Dict, Any
from enum import Enum, auto


class TemplateVariance(Enum):
    """模板参数的协变性"""

    INVARIANT = auto()  # 不变
    COVARIANT = auto()  # 协变
    CONTRAVARIANT = auto()  # 逆变


@dataclass
class TemplateParameter:
    """模板参数定义"""

    name: str
    bound: Optional[str] = None  # 类型约束：T extends SomeClass
    constraints: List[str] = field(default_factory=list)  # 类型约束：T in (int, str)
    variance: TemplateVariance = TemplateVariance.INVARIANT
    default_type: Optional[str] = None  # 默认类型

    def to_cpp_template_param(self) -> str:
        """转换为 C++ 模板参数声明"""
        if self.bound:
            # template<typename T> requires std::derived_from<T, BaseClass>
            return f"typename {self.name}"
        elif self.constraints:
            # template<typename T> requires (std::same_as<T, int> || std::same_as<T, str>)
            return f"typename {self.name}"
        else:
            # template<typename T>
            return f"typename {self.name}"

    def to_cpp_constraint(self) -> Optional[str]:
        """生成 C++20 约束条件"""
        if self.bound:
            return f"std::derived_from<{self.name}, {self.bound}>"
        elif self.constraints:
            constraint_checks = [
                f"std::same_as<{self.name}, {c}>" for c in self.constraints
            ]
            return f"({' || '.join(constraint_checks)})"
        return None


class TemplateParser:
    """模板参数解析器"""

    @staticmethod
    def extract_template_params_from_function(func_node) -> List[TemplateParameter]:
        """从函数定义中提取模板参数"""
        template_params = []

        # 检查函数体中的 IRTypeVar 节点
        if hasattr(func_node, "body"):
            for stmt in func_node.body:
                if hasattr(stmt, "node_type") and stmt.node_type == "typevar":
                    # 这是一个 IRTypeVar 节点
                    param = TemplateParameter(
                        name=stmt.name,
                        bound=stmt.bound,
                        constraints=stmt.constraints,
                        variance=(
                            TemplateVariance.COVARIANT
                            if stmt.covariant
                            else (
                                TemplateVariance.CONTRAVARIANT
                                if stmt.contravariant
                                else TemplateVariance.INVARIANT
                            )
                        ),
                    )
                    template_params.append(param)

        return template_params

    @staticmethod
    def extract_template_params_from_class(class_node) -> List[TemplateParameter]:
        """从类定义中提取模板参数"""
        template_params = []

        # 检查类体中的 IRTypeVar 节点
        if hasattr(class_node, "body"):
            for stmt in class_node.body:
                if hasattr(stmt, "node_type") and stmt.node_type == "typevar":
                    # 这是一个 IRTypeVar 节点
                    param = TemplateParameter(
                        name=stmt.name,
                        bound=stmt.bound,
                        constraints=stmt.constraints,
                        variance=(
                            TemplateVariance.COVARIANT
                            if stmt.covariant
                            else (
                                TemplateVariance.CONTRAVARIANT
                                if stmt.contravariant
                                else TemplateVariance.INVARIANT
                            )
                        ),
                    )
                    template_params.append(param)

        return template_params


class TemplateCodeGenerator:
    """模板代码生成器"""

    @staticmethod
    def generate_template_declaration(template_params: List[TemplateParameter]) -> str:
        """生成 C++ 模板声明"""
        if not template_params:
            return ""

        param_decls = [param.to_cpp_template_param() for param in template_params]
        template_decl = f"template<{', '.join(param_decls)}>"

        # 添加约束条件（C++20）
        constraints = [
            param.to_cpp_constraint()
            for param in template_params
            if param.to_cpp_constraint()
        ]
        if constraints:
            template_decl += f"\nrequires {' && '.join(constraints)}"

        return template_decl

    @staticmethod
    def generate_template_instantiation(
        template_name: str, type_args: List[str]
    ) -> str:
        """生成模板实例化"""
        if not type_args:
            return template_name
        return f"{template_name}<{', '.join(type_args)}>"


# 全局模板参数注册表
_global_template_registry: Dict[str, TemplateParameter] = {}


def register_template_parameter(name: str, param: TemplateParameter):
    """注册全局模板参数"""
    _global_template_registry[name] = param


def get_template_parameter(name: str) -> Optional[TemplateParameter]:
    """获取已注册的模板参数"""
    return _global_template_registry.get(name)


def clear_template_registry():
    """清空模板参数注册表"""
    _global_template_registry.clear()


def replace_template_params(template: str, signature, param_codes: list[str]) -> str:
    """根据签名中的参数名替换模板占位符

    这是一个通用的模板参数替换函数，用于将模板字符串中的参数占位符
    （如 {self}, {n}, {other}）替换为实际生成的代码。

    Args:
        template: 模板字符串，如 "mcpy::list_repeat({self}, {n})"
        signature: Signature 对象，包含参数名列表（signature.args）
        param_codes: 生成的参数代码列表，按参数顺序

    Returns:
        替换后的代码字符串

    Example:
        >>> template = "mcpy::list_repeat({self}, {n})"
        >>> signature.args = ["self", "n"]
        >>> param_codes = ["my_list", "3"]
        >>> replace_template_params(template, signature, param_codes)
        'mcpy::list_repeat(my_list, 3)'
    """
    code = template

    # 从签名中获取参数名列表
    param_names = getattr(signature, "args", [])

    # 替换每个参数
    for i, (param_name, param_code) in enumerate(zip(param_names, param_codes)):
        # 同时支持单层和双层大括号
        # 双层大括号用于用户自定义类，单层用于内置类型
        code = code.replace(f"{{{{{param_name}}}}}", param_code)
        code = code.replace(f"{{{param_name}}}", param_code)

    return code
